/*
 * DocTable.java - SQL operations with the table doc in ksnetcontext (weighted 
 *  document linking to ontology classes and attributes).
 *  See more in webdeso_gate.addon/sqldump
 *      - ksnetcontext.sql  - structure of the database,
 *      - readme.txt        - commands example
 *
 * Copyright (c) 2006-2007 Computer-Aided Integrated Systems Laboratory, 
 * St. Petersburg Institute for Informatics and Automation RAS http://cais.lisa.iias.spb.su
 */

package dbindex;

import wikipedia.sql.PageTable;
import wikipedia.util.StringUtil;
import wikipedia.util.Encodings;
import wikipedia.sql.PageNamespace;

import java.sql.Statement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Map;
import java.util.HashMap;

/** Corresponds to the tables 'doc' and 'doc_sim' in 'ksnetcontext' database,
 * table 'doc' describes properties of the document: title, filepath, url;
 * table 'doc_sim' describes properties of the similarity between document and 
 *                 ontology: doc_id, onto_id, sim;
 */
public class DocTable {
    
    /** Creates a new instance of DocTable */
    public DocTable() {
        sim = -1;
        relevance = -1;
    }
    
    private final static DocTable[]   NULL_DOCTABLE_ARRAY  = new DocTable[0];

    /** document's id (autoincremental in the table doc) */
    public int      doc_id;
    
    /** document's title */
    public String   title;
    
    /** location of the document */
    public String   filepath;
    
    /** URI of the document */
    public String   url;
    
    /** similarity between application ontology fragment and the document, stored in Index */
    public double   sim;
    
    /** language identifier: English, Russian */
    public Language lang;
    
    /** similarity (relevance) between application ontology fragment (relevant to document) and ontology fragment (context) */
    public double   relevance;
    
    
    /** ID of ontology that is compared against the document metadata or document text 
     * (so, similarity value is meaningfull only when the ontology is known) */
//    public int      onto_id;
            
    
    
    /** SQL examples:
     * 1) 
     * work
DELETE FROM ksnetcontext.doc WHERE title="DocTable.testUpdateByTitle" AND (filepath="disk e:" OR url="punto.ru")
INSERT INTO ksnetcontext.doc (title,filepath,url) VALUES('DocTable.testUpdateByTitle','disk e:','punto.ru')
     *
     * test it:
SELECT id FROM ksnetcontext.doc WHERE title="tale" and filepath="" and url="http://";
SELECT * FROM ksnetcontext.doc WHERE title="DocTable.testUpdateByTitle" OR filepath="disk e:" OR url="punto.ru";
     *
     * failed: 
REPLACE INTO ksnetcontext.doc (title,filepath,url) VALUES("tale","","http://");
INSERT INTO ksnetcontext.doc (title,filepath,url) VALUES('DocTable.testUpdateByTitle','disk e:','punto.ru') ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id);
SELECT * FROM ksnetcontext.doc WHERE title='DocTable.testUpdateByTitle' AND filepath='disk e:' AND url='punto.ru';
     * @see http://dev.mysql.com/doc/refman/5.1/en/insert-on-duplicate.html
     * 
     * 2) insert onto_id ans sim to 'doc_sim'
REPLACE INTO ksnetcontext.doc_sim (doc_id,onto_id,sim) VALUES(1182, 3333,0.33);
     *
     * check it: 
SELECT * FROM ksnetcontext.doc_sim WHERE doc_id=1182 OR onto_id=3333 OR sim=0.33;
     */
    
    /** Updates records in the tables 'doc' and 'doc_sim'
     *  
     *  @param title        document's title
     *  @param filepath     location of the document
     *  @param sim          similarity between application ontology fragment and the document with new assigned doc_id
     *  @param lang         document's language 
     *  
     *  @return doc.id (autoincrement)
     */
    
    public static int UpdateByTitle (java.sql.Connection conn,Language lang,
            String title,String filepath,String url,double sim,int onto_id) {
        boolean b_insert_new = false;
        return ChangeByTitle (conn, lang, title, filepath, url, sim, onto_id, b_insert_new); //"REPLACE", new String(" WHERE doc_id=" +doc_id));
    }
    
    /** Inserts records to the tables 'doc' and 'doc_sim' (delete previous record).
     *  @return doc.id (autoincrement)
     */
    public static int InsertByTitle (java.sql.Connection conn,Language lang,
            String title,String filepath,String url,double sim,int onto_id) {
        boolean b_insert_new = true;
        return ChangeByTitle (conn, lang, title, filepath, url, sim, onto_id, b_insert_new); //"INSERT IGNORE", "");
    }
    
    /** Inserts or updates a record in the table doc, inserts if 'b_insert_new' 
     * is true, else updates. If old record is absent then inserts new record.
     *
     *  @return doc.id (autoincrement)
     */
    public static int ChangeByTitle (java.sql.Connection conn,Language lang,
            String title,String filepath,String url,double sim,int onto_id,
            boolean b_insert_new) {
        int doc_id_old = 0;
        int doc_id     = 0;
        
        Statement   s = null;
        ResultSet   rs= null;
        int         size = 0;
        String      str_sql = null;
        try {
            s = conn.createStatement ();
            
            title   = StringUtil.escapeChars(title);
            filepath= StringUtil.escapeChars(filepath);
            url     = StringUtil.escapeChars(url);
                    
            // delete records in doc_sim, comp_class, comp_attr with old (doc_id & onto_id)
            String _filepath = (0 == filepath.length()) ? "" : "and filepath='"+filepath+"' ";
            String str_select_doc_id = "SELECT id FROM doc WHERE title='"+title+"' " + _filepath + "and url='"+url+"'";

            s.executeQuery (str_select_doc_id);
            rs = s.getResultSet ();
            if (rs.next ())
            {
                doc_id_old = rs.getInt("id");
                str_sql = "DELETE IGNORE FROM doc_sim WHERE doc_id="   +doc_id_old+" AND onto_id="+onto_id; s.execute (str_sql);
                str_sql = "DELETE IGNORE FROM comp_class WHERE doc_id="+doc_id_old+" AND onto_id="+onto_id; s.execute (str_sql);
                str_sql = "DELETE IGNORE FROM comp_attr WHERE doc_id=" +doc_id_old+" AND onto_id="+onto_id; s.execute (str_sql);
            } else {
                b_insert_new = true;
            }
            
            if(b_insert_new) {
                
                // deletes record in doc and gets new autoincremental doc_id
                str_sql = "DELETE IGNORE FROM doc WHERE title='" +
                                       title + "' AND (filepath='" +
                                             filepath + "' OR url='" +
                                                      url + "')";
                s.execute (str_sql);
            
                str_sql = "INSERT IGNORE INTO doc (title,filepath,url) VALUES('" +
                                                   title + "','" +
                                                         filepath + "','" +
                                                                  url + "')";
            } else {
                //str_sql =       "REPLACE INTO doc (title,filepath,url) VALUES('";
                //str_sql = "UPDATE IGNORE doc SET title='DocTable.testUpdateByTitle', filepath='disk X:', url='punto.ru' WHERE id=4923
                str_sql ="UPDATE IGNORE doc SET title='"    + title + 
                                            "', filepath='" + filepath  + 
                                            "', url='"      + url       +
                          "' WHERE id="+doc_id_old;
            }
            
            s.execute (str_sql);
            
            // add data to doc_sim
            s.executeQuery (str_select_doc_id);
            rs = s.getResultSet ();
            doc_id = doc_id_old;    // it's valid if there is no records in doc_sim
            if (rs.next ())
            {
                doc_id = rs.getInt("id");
                str_sql = "REPLACE INTO doc_sim (doc_id,onto_id,sim) VALUES("+
                                            doc_id + "," + onto_id + ","+ sim + ")";
                s.execute (str_sql);
                
                // update doc_sim, comp_class, comp_attr with new doc_id
                // e.g., UPDATE doc_sim SET doc_id=1111 WHERE doc_id=1110
                str_sql = "UPDATE doc_sim SET doc_id="    + doc_id + " WHERE doc_id=" + doc_id_old; s.execute (str_sql);
                str_sql = "UPDATE comp_class SET doc_id=" + doc_id + " WHERE doc_id=" + doc_id_old; s.execute (str_sql);
                str_sql = "UPDATE comp_attr SET doc_id="  + doc_id + " WHERE doc_id=" + doc_id_old; s.execute (str_sql);
            }
            
        } catch(SQLException ex) {
            System.err.println("SQLException (ksnetcontext DocTable.java UpdateByTitle()):: sql='" + str_sql + "' " + ex.getMessage());
        } finally {
            if (rs != null) {   try { rs.close(); } catch (SQLException sqlEx) { }  rs = null; }
            if (s != null)  {   try { s.close();  } catch (SQLException sqlEx) { }  s = null;  }
        }
        
        return doc_id;
    }
    
    public static void UpdateByID (java.sql.Connection conn,Language lang,
                                   int doc_id,
                                   int onto_id, double sim) {

    	Statement   s = null;
        ResultSet   rs= null;
        int         size = 0;
        String      str_sql = null;
        try {
            s = conn.createStatement ();
            
            String str_select_doc_id = "SELECT id FROM doc_sim WHERE doc_id = "+ doc_id +" and onto_id =" + onto_id;
            s.executeQuery (str_select_doc_id);
            rs = s.getResultSet ();
            if (rs.next ())
            {
                str_sql = "UPDATE doc_sim set sim = " + sim + " WHERE doc_id = "+ doc_id +" and onto_id =" + onto_id; 
                s.executeUpdate (str_sql);
            }
            else
            {
                str_sql = "INSERT IGNORE INTO doc_sim (doc_id,onto_id,sim) VALUES(" + doc_id + "," + onto_id +"," + sim + ")";
                s.execute (str_sql);

            }
            
        } catch(SQLException ex) {
            System.err.println("SQLException (ksnetcontext DocTable.java UpdateByID()):: sql='" + str_sql + "' " + ex.getMessage());
        } finally {
            if (rs != null) {   try { rs.close(); } catch (SQLException sqlEx) { }  rs = null; }
            if (s != null)  {   try { s.close();  } catch (SQLException sqlEx) { }  s = null;  }
        }
    }
    /*
    public static int Insert (java.sql.Connection conn,String title,String filepath,String url,double sim) {
        int doc_id = 0;
        
        Statement   s = null;
        ResultSet   rs= null;
        int         size = 0;
        String      str_sql = null;
        try {
            s = conn.createStatement ();
            
            str_sql = "REPLACE INTO doc (title,filepath,url,sim) VALUES('"+
                                        title + "','" +
                                               filepath + "','"+
                                                      url + "',"+
                                                          sim + ")"; 
            s.executeQuery (str_sql);
            
            str_sql = "SELECT LAST_INSERT_ID() AS doc_id";
            s.executeQuery (str_sql);
            rs = s.getResultSet ();
            if (rs.next ())
            {
                doc_id = rs.getInt("doc_id");
            }
        } catch(SQLException ex) {
            System.err.println("SQLException (ksnetcontext DocTable.java Insert()):: sql='" + str_sql + "' " + ex.getMessage());
        } finally {
            if (rs != null) {   try { rs.close(); } catch (SQLException sqlEx) { }  rs = null; }
            if (s != null)  {   try { s.close();  } catch (SQLException sqlEx) { }  s = null;  }
        }
        
        return doc_id;
    }*/
    
    
    /* Takes id of Wikipedia indexed documents d by its 'title'.
    */
    /*public static int GetWikiDocIdByTitle(String title) {
        
        return 0;
    }*/
    
    /* Takes id of local indexed documents d by its 'title'.
     */
    /*public static int GetLocalDocIdByTitle(String title) {
        // todo
        return -1;
    }*/
    
    /** Selects (title,filepath,sim) from the table doc where 'filepath' exists at local drive.
     *
     *  SQL:
     *  SELECT * FROM doc;
     *
     *  @param doc_id       documents id
     *  @param conn         initialized connection to ksnetcontext DB
     *  @return DocTable    object with initialized fields: title,filepath,sim
     */
    public static DocTable[] GetAllExistedLocalDocs (java.sql.Connection conn) {
        
        //File f = new File(rs.getString("filepath"));
        //if (f.exists()) {...}
        
        return null;
    }
    
    /** Gets substring of str after last occurence of symbol in str. */
    public static String GetTextAfterLastSymbol (String str,String symbol) {
        if(null == str) {
            return "";
        }
        
        int n_last = str.lastIndexOf(symbol);
        if(-1 == n_last) {
            return str;
        } else {
            return str.substring(n_last+1);
        }
    }
    /** Selects (title,filepath,sim) from the table doc where 'url' exists in 
     * Wikipedia wp_conn (local or remote Wikipedia).
     *
     *old:
     * Selects (title,filepath,sim) from the table doc where 'title' exists in Wikipedia wp_conn.
     *
     *  SQL:
     *  SELECT * FROM doc;
     *
     *  @param conn         initialized connection to ksnetcontext DB
     *  @param wp_conn      initialized connection to Wikipedia DB
     *  @return DocTable    objects' array with initialized fields: title,filepath,sim
     */
    public static DocTable[] GetAllExistedInWikipedia (
            java.sql.Connection conn, Language lang,
            wikipedia.sql.Connect wp_conn) {
    
        if (null == wp_conn) {
            System.out.println("Error: There is no connection to Wikipedia. Use ConnectWikipedia(). DocTable.GetAllExistedInWikipedia().");
            return null;
        }
                
        List<DocTable> dd = new ArrayList<DocTable>();
        Encodings e = new Encodings();
        
        Statement   s = null;
        ResultSet   rs= null;
        String      str_sql = null;
        try {
            s = conn.createStatement ();
            
            str_sql = "SELECT * FROM doc WHERE length(url) > 0 and lang_id=" + lang.toInt();
            //str_sql = "SELECT * FROM doc";
            //str_sql = "SELECT * FROM ksnetcontext.doc, ksnetcontext.doc_sim " +
            //          "WHERE ksnetcontext.doc.id=ksnetcontext.doc_sim.doc_id";
            s.executeQuery (str_sql);
            
            rs = s.getResultSet ();
            while (rs.next ())
            {
                String url = rs.getString("url");     
                //byte[] b = rs.getBytes("url");
                //String url = null == b ? "" : Encodings.bytesTo(b, "UTF8");
                
                String page_title = DocTable.GetTextAfterLastSymbol(url, "/");
                //System.out.println("url="+url+"; page_title="+page_title);
                
                if(null != page_title && 0 < page_title.length()) {
                    int page_id = PageTable.getIDByTitleNamespace(wp_conn, page_title, PageNamespace.MAIN);
  /*                  
            String t = page_title;
            //t = connect_ru.enc.EncodeFromJava(t);
            int id = PageTable.GetIDByTitleNamespace(wp_conn, t, PageNamespace.MAIN);
            
            int id1 = PageTable.getIDByTitleNamespace(wp_conn, Encodings.FromTo(t, "UTF8",      "ISO8859_1"), PageNamespace.MAIN);
            int id2 = PageTable.getIDByTitleNamespace(wp_conn, Encodings.FromTo(t, "ISO8859_1", "UTF8"), PageNamespace.MAIN);
            int id3 = PageTable.getIDByTitleNamespace(wp_conn, Encodings.FromTo(t, "Cp1251",    "UTF8"), PageNamespace.MAIN);
            int id4 = PageTable.getIDByTitleNamespace(wp_conn, Encodings.FromTo(t, "UTF8",      "Cp1251"), PageNamespace.MAIN);
            int id5 = PageTable.getIDByTitleNamespace(wp_conn, Encodings.FromTo(t, "Cp1251",    "ISO8859_1"), PageNamespace.MAIN);
            int id6 = PageTable.getIDByTitleNamespace(wp_conn, Encodings.FromTo(t, "ISO8859_1", "Cp1251"), PageNamespace.MAIN);
*/                    
                    if(0 != page_id) {
                        DocTable d = new DocTable();
                        d.doc_id    = rs.getInt("id");
                        d.filepath  = rs.getString("filepath");
                        d.url       = url;
                        //d.sim       = rs.getFloat("sim");
                        //d.onto_id   = rs.getInt("onto_id");
                        d.title     = page_title;
                        d.lang      = lang;
                        dd.add(d);
                    }
                }
            }
        } catch(SQLException ex) {
            System.err.println("SQLException (ksnetcontext DocTable.java GetAllExistedInWikipedia()):: sql='" + str_sql + "' " + ex.getMessage());
        } finally {
            if (rs != null) {   try { rs.close(); } catch (SQLException sqlEx) { }  rs = null; }
            if (s != null)  {   try { s.close();  } catch (SQLException sqlEx) { }  s = null;  }
        }
        
        return (DocTable[])dd.toArray(NULL_DOCTABLE_ARRAY);
    }
    
    
    
    
    
    /** Copies Wikipedia page.title (articles) to ksnetcontext /table doc(title,url)/.
     * I.e. selects titles of articles from Wikipedia and put them to ksnetcontext_ru.doc(title,url).
     * Redirect titles Wikipedia are skipped.
     * <PRE>SQL sequence:
     * (1) delete in ksnetcontext records about documents with NON empty url: 
     * DELETE FROM doc_sim WHERE doc_id IN (SELECT id FROM doc WHERE 0 < length(url))
     * DELETE FROM comp_attr WHERE doc_id IN (SELECT id FROM doc WHERE 0 < length(url));
     * DELETE FROM comp_class WHERE doc_id IN (SELECT id FROM doc WHERE 0 < length(url));
     * DELETE FROM domain_value_attr WHERE doc_id IN (SELECT id FROM doc WHERE 0 < length(url))
     * DELETE FROM doc WHERE 0 < length(url)
     *
     * (2) Wikipedia SQL:    SELECT page_title FROM page WHERE page_namespace=0 LIMIT 10 
     *                                                                       (0=PageNamespace.MAIN)</PRE>
     *
     *  @param conn         initialized connection to ksnetcontext DB
     *  @param wp_conn      initialized connection to Wikipedia DB
     *  @param doc_url_prefix   the prefix to be added to the field url 
     *          in the table doc in ksnetcontext(_ru), 
     *          e.g http://ru.wikipedia.org/wiki/
     *  @param n_limit  number of titles of Wikipedia articles to be retrieved,
     *                  -1 means that there is no limit
     *  @return true if there is no exception
     */
    public static boolean copyWPTitlesToDocTitle (
            java.sql.Connection   ksn_conn, String doc_url_prefix,
            wikipedia.sql.Connect wp_conn,  int n_limit) {
    
        if (null == wp_conn) {
            System.out.println("Error: There is no connection to Wikipedia. Use ConnectWikipedia(). DocTable.copyWPTitlesToDocTitle().");
            return false;
        }
        if (null == ksn_conn) {
            System.out.println("Error: There is no connection to ksnetcontext(_ru). Use Index.getDBConnection(). DocTable.copyWPTitlesToDocTitle().");
            return false;
        }
        
        boolean b_res = true;
        String      str_sql = "";
        Statement   ksn_s = null, wp_s = null;
        ResultSet   rs = null;
        try {
            ksn_s = ksn_conn.createStatement ();
            ksn_s.execute ("DELETE FROM doc_sim WHERE doc_id IN (SELECT id FROM doc WHERE 0 < length(url))");
            ksn_s.execute ("DELETE FROM comp_attr WHERE doc_id IN (SELECT id FROM doc WHERE 0 < length(url))");
            ksn_s.execute ("DELETE FROM comp_class WHERE doc_id IN (SELECT id FROM doc WHERE 0 < length(url))");
            ksn_s.execute ("DELETE FROM domain_value_attr WHERE doc_id IN (SELECT id FROM doc WHERE 0 < length(url))");
            ksn_s.execute ("DELETE FROM doc WHERE 0 < length(url)");
            
            Encodings e = wp_conn.enc;
            String db_enc = e.GetDBEnc();
            
            System.out.println("Copying Wikipedia titles of articles to ksnetcontext(_ru).doc.(title,url) is started...");
            int n_counter = 0;
            
            str_sql = "SELECT page_title FROM page WHERE page_namespace=0 AND page_is_redirect!=1";
            str_sql += (n_limit > 0) ? " LIMIT " + n_limit : "";
            wp_s  = wp_conn.conn.createStatement ();
            wp_s.executeQuery (str_sql);
            
            rs = wp_s.getResultSet ();
            while (rs.next ())
            {
                //title = Encodings.bytesTo(rs.getBytes("page_title"), e.GetDBEnc());
                String db_str = Encodings.bytesTo(rs.getBytes("page_title"), db_enc);
//System.out.println("db_str="+db_str);
                //String t = e.EncodeFromDB(db_str);
                String t = StringUtil.spaceToUnderscore(StringUtil.escapeChars(e.EncodeFromDB(db_str)));
//System.out.println("t="+t);
                //str_sql = "INSERT IGNORE INTO doc (title,url) VALUES('" + t + "','" + doc_url_prefix + t +"')";
                //str_sql = "REPLACE INTO doc (title,url) VALUES('" + t + "','" + doc_url_prefix + t +"')";
                str_sql = "INSERT IGNORE INTO doc (title,url) VALUES('" + t + "','" + doc_url_prefix + t +"')";
//System.out.println("str_sql="+str_sql);
                ksn_s.execute (str_sql);
                n_counter ++;
                if(0 == n_counter % 1000)
                    System.out.println(n_counter);
            }
        } catch(SQLException ex) {
            System.err.println("SQLException (wiki->ksnetcontext DocTable.java copyWPTitlesToDocTitle()):: " + ex.getMessage());
            b_res = false;
        } finally {
            if (rs   != null) {   try { rs.close();     } catch (SQLException sqlEx) { }  rs    = null; }
            if (ksn_s!= null) {   try { ksn_s.close();  } 
                                  catch (SQLException ex) {
                                      System.err.println("SQLException (wiki->ksnetcontext DocTable.java copyWPTitlesToDocTitle()):: sql='" + str_sql + "' " + ex.getMessage());
                                  }  ksn_s = null;  }
            if (wp_s != null) {   try { wp_s.close();   } catch (SQLException sqlEx) { }  wp_s  = null;  }
        }
        
        return b_res;
    }
    
    
    
    
    
    /** Selects (title,filepath,sim) from the table doc by the documents id.
     *
     *  SQL:
     *  SELECT * FROM doc WHERE id=6;
     *
     *  @param doc_id       documents id
     *  @return DocTable    object with initialized fields: title,filepath,sim
     */
    public static DocTable GetByDocID (java.sql.Connection conn,int doc_id,int onto_id) {
        DocTable d = null;
        
        Statement   s = null;
        ResultSet   rs= null;
        int         size = 0;
        String      str_sql = null;
        try {
            s = conn.createStatement ();
            
            str_sql = "SELECT * FROM doc WHERE id=" + doc_id;
            s.executeQuery (str_sql);
            
            rs = s.getResultSet ();
            if (rs.next ())
            {
                d = new DocTable();
                d.doc_id    = doc_id;
                d.filepath  = rs.getString("filepath");
                d.url       = rs.getString("url");
                d.title     = rs.getString("title");
                d.lang = Language.get(rs.getInt("lang_id"));
            
                str_sql = "SELECT * FROM doc_sim WHERE doc_id="+doc_id+" AND onto_id="+onto_id;
                s.executeQuery (str_sql);

                rs = s.getResultSet ();
                if (rs.next ())
                {
                    d.sim       = rs.getFloat("sim");
                }
            }
        } catch(SQLException ex) {
            System.err.println("SQLException (ksnetcontext DocTable.java GetByDocID()):: sql='" + str_sql + "' " + ex.getMessage());
        } finally {
            if (rs != null) {   try { rs.close(); } catch (SQLException sqlEx) { }  rs = null; }
            if (s != null)  {   try { s.close();  } catch (SQLException sqlEx) { }  s = null;  }
        }
        
        return d;
    }
    
    public static List<DocTable> getDocsWithUrlByClassIDAttrID(
            java.sql.Connection conn, Language lang,
            int[] class_id,int[] attr_id,int onto_id) {
        
        int[] docs_id = ComparativeClass.getDocIDWithUrlByClassID(conn, class_id, lang);
        if(null == docs_id || 0 == docs_id.length)
            return null;
        
        //int[] docs_id = ComparativeAttr.getDocIDByAttrID (conn, attr_id);
        // todo
        // ..
        
        List<DocTable> l = new ArrayList<DocTable>();
        for(int id:docs_id) {
            DocTable dt = GetByDocID (conn, id, onto_id);
            if(null != dt) {
                l.add(dt);
            }
        }
        
        return l;
    }

    // --- Non database function ---
    
    public static final Comparator<DocTable> DIST_ORDER = new Comparator<DocTable>() {
        public int compare(DocTable e1, DocTable e2) {
            if (e1.relevance > e2.relevance)
                return -1;
            return 1;
        }
    };
    
    /** Creates map from document title to the document for each item in docs array */
    public static Map<String, DocTable> createMapTitleToDoc(DocTable[] docs) {
        if(null == docs || 0 == docs.length) {
            return null;
        }
        Map<String, DocTable> title_to_doc = new HashMap<String, DocTable>();
        for(DocTable d:docs) {
            title_to_doc.put(d.title, d);
        }
        return title_to_doc;
    }
    
    /** Normalizes (proportionally) the value of relevance, so there is at 
     * least one document with relevance about 1.0 after the normalization.
     */
    public static void normalizeRelevance(List<DocTable> docs) {
        double max = Double.NEGATIVE_INFINITY;
        for(DocTable d : docs) {
            if(d.relevance > max) {
               max = d.relevance;
            }
        }
        if(max <= 0 || max >= 1)
            return;
        
        double mult = 1.d / max;
        
        for(DocTable d : docs) {
            d.relevance *= mult;
        }
    };
}


